import {
  HttpException,
  HttpStatus,
  Inject,
  Injectable,
  NotFoundException,
  Scope,
} from '@nestjs/common';
import { ConfigService, ConfigType } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto';
import { Event } from 'src/events/entities/event.entity';
import { DataSource, Repository } from 'typeorm';
import coffeesConfig from './coffees.config';
import { COFFEES_BRANDS } from './coffees.constants';
import { CreateCoffeeDto } from './dto/create-coffee.dto';
import { UpdateCoffeeDto } from './dto/update-coffee.dto';
import { Coffee } from './entities/coffee.entity';
import { Flavor } from './entities/flavor.entity';

@Injectable({
  scope: Scope.DEFAULT,
  // scope: Scope.TRANSIENT,
  // scope: Scope.REQUEST,
})
export class CoffeesService {
  constructor(
    @InjectRepository(Coffee)
    private readonly coffeeRepository: Repository<Coffee>,
    @InjectRepository(Flavor)
    private readonly flavorRepository: Repository<Flavor>,
    // 事物 https://docs.nestjs.com/techniques/database#transactions
    private readonly dataSource: DataSource,
    @Inject(COFFEES_BRANDS) coffeeBrands: string[],
    private readonly configService: ConfigService,
    @Inject(coffeesConfig.KEY)
    injectedCoffeesConfig: ConfigType<typeof coffeesConfig>,
  ) {
    console.log('initial');
    // 注意这里都是字符串类型，configService并不会做类型转换
    const dbHost = this.configService.get<string>('DB_HOST');
    console.log(dbHost);
    // const dbHost2 = this.configService.get<string>('database.host');
    // console.log(dbHost2);
    const coffeeC = this.configService.get('coffees');
    console.log('coffeeC', coffeeC);
    console.log('injectedCoffeesConfig', injectedCoffeesConfig);
    // console.log('Scope.TRANSIENT ', Scope.TRANSIENT);
    // console.log('Scope.REQUEST', Scope.REQUEST);
    // console.log(COFFEES_BRANDS);
    console.log('Inject', COFFEES_BRANDS, coffeeBrands);
  }

  async findAll(paginationQuery: PaginationQueryDto, timeout?: boolean) {
    const { offset, limit } = paginationQuery;
    if (timeout) {
      await new Promise((res) => setTimeout(res, 5e3));
    }
    return this.coffeeRepository.find({
      relations: ['flavors'],
      skip: offset,
      take: limit,
    });
  }

  async findOne(id: number) {
    const coffee = await this.coffeeRepository.findOne({
      where: {
        id,
      },
      relations: ['flavors'],
    });

    console.log('查不到', coffee);
    if (!coffee) {
      throw new HttpException(
        `coffee ${id} is not found ${HttpStatus.NOT_FOUND}`,
        HttpStatus.NOT_FOUND,
      );
    }
    return coffee;
  }

  async create(createCoffeeDto: CreateCoffeeDto) {
    const flavors = await Promise.all(
      createCoffeeDto.flavors.map((name) => this.preloadFlavorByName(name)),
    );
    const coffee = this.coffeeRepository.create({
      ...createCoffeeDto,
      flavors,
    });
    return this.coffeeRepository.save(coffee);
  }

  async update(id: number, updateCoffeeDto: UpdateCoffeeDto) {
    console.log(updateCoffeeDto);
    const flavors = await Promise.all(
      updateCoffeeDto.flavors.map((name) => this.preloadFlavorByName(name)),
    );
    const coffee = await this.coffeeRepository.preload({
      id,
      ...updateCoffeeDto,
      flavors,
    });
    console.log(coffee);

    if (!coffee) {
      throw new NotFoundException(`Coffee ${id} not found`);
    }
    return this.coffeeRepository.save(coffee);
  }

  async remove(id: number) {
    const coffee = await this.findOne(id);
    return this.coffeeRepository.remove(coffee);
  }

  private async preloadFlavorByName(name: string): Promise<Flavor> {
    const existingFlavor = await this.flavorRepository.findOne({
      where: {
        name,
      },
    });

    if (existingFlavor) {
      return existingFlavor;
    }
    return this.flavorRepository.create({ name });
  }

  async recommendCoffee(coffee: Coffee) {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      coffee.recommendations++;
      const recommendEvent = new Event();
      recommendEvent.name = 'recommend_coffee';
      recommendEvent.type = 'coffee';
      recommendEvent.payload = { coffeeId: coffee.id };
      await queryRunner.manager.save(coffee);
      await queryRunner.manager.save(recommendEvent);

      await queryRunner.commitTransaction();
    } catch (err) {
      // since we have errors lets rollback the changes we made
      await queryRunner.rollbackTransaction();
    } finally {
      // you need to release a queryRunner which was manually instantiated
      await queryRunner.release();
    }
  }
}
